﻿// *********************************************************************
// [DCOM Productions]
// [Copyright (C) DCOM Productions All rights reserved.]
// *********************************************************************

namespace BitFlex.Xdf {
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// Class used to create and build Xdf strings
    /// </summary>
    public class XdfBuilder {
        /// <summary>
        /// Initializes a new instance of the BitFlex.Xdf.XdfBuilder class
        /// </summary>
        public XdfBuilder() {
            m_Buffer = new StringBuilder();
            m_CurrentDepthLevel = 0;
            m_UnclosedSections = new Stack<string>();
        }

        #region Fields

        /// <devdoc>
        /// Buffer used to store the xdf string when using the Write methods
        /// </devdoc>
        private StringBuilder m_Buffer;

        /// <devdoc>
        /// Private field that stores the level of depth in the current xdf string
        /// Example:
        ///     0   SectionName:Begin
        ///     1       ChildName:Begin
        ///     2           Key=A
        ///     2           Key=B
        ///     1       ChildName:End
        ///     0   SectionName:End
        /// The depth level also determines the amount of indent spacing used for each depth
        /// Example:
        ///     0   m_CurrentDepthLevel * m_IndentSpacing   =   0 spaces
        ///     1   m_CurrentDepthLevel * m_IndentSpacing   =   4 spaces
        ///     2   m_CurrentDepthLevel * m_IndentSpacing   =   8 spaces
        /// And so on
        /// </devdoc>
        private int m_CurrentDepthLevel;

        /// <devdoc>
        /// Stack used to store a collection of sections that have not been closed by calling WriteEndSection()
        /// </devdoc>
        private Stack<string> m_UnclosedSections;

        #endregion

        #region Properties

        private int m_IndentDepth = 4;
        /// <summary>
        /// Gets or sets the amount of spacing in each depth level during formatting
        /// </summary>
        public int IndentDepth {
            get {
                return m_IndentDepth;
            }
            set {
                m_IndentDepth = value;
            }
        }

        #endregion

        /// <summary>
        /// Flushes the stream and clears the buffer to start a new Xdf string
        /// </summary>
        public void Flush() {
            m_Buffer = new StringBuilder();
        }

        /// <summary>
        /// Returns the xdf string in the buffer
        /// </summary>
        public override string ToString() {
            return m_Buffer.ToString();
        }

        /// <summary>
        /// Writes the specified key and its associated value to the buffer
        /// </summary>
        /// <param name="name">The name of the key</param>
        /// <param name="value">The key's value</param>
        public void WriteKey(string name, object value) {
            int pos = 0;
            while (pos != (m_CurrentDepthLevel * this.IndentDepth)) {
                m_Buffer.Append(' ');
                pos++;
            }
            m_Buffer.AppendLine(string.Format("{0} = {1}", name, value));
        }

        /// <summary>
        /// Writes the specified <see cref="XdfKey"/> to the buffer
        /// </summary>
        /// <param name="key">The <see cref="XdfKey"/> to write</param>
        public void WriteKey(XdfKey key) {
            WriteKey(key.Name, key.Value);
        }

        /// <summary>
        /// Writes the beginning of a new section to the buffer
        /// </summary>
        /// <param name="name">The name of the section</param>
        public void WriteBeginSection(string name) {
            int pos = 0;
            while (pos != (m_CurrentDepthLevel * this.IndentDepth)) {
                m_Buffer.Append(' ');
                pos++;
            }
            m_Buffer.AppendLine(string.Format("{0}:Begin", name));
            m_UnclosedSections.Push(name);
            m_CurrentDepthLevel++;
        }

        /// <summary>
        /// Writes the closing tag for an open section to the buffer
        /// </summary>
        public void WriteEndSection() {
            if (m_UnclosedSections.Count > 0) {
                m_CurrentDepthLevel--;
                int pos = 0;
                while (pos != (m_CurrentDepthLevel * this.IndentDepth)) {
                    m_Buffer.Append(' ');
                    pos++;
                }
                m_Buffer.AppendLine(string.Format("{0}:End", m_UnclosedSections.Pop()));
            }
        }

        /// <summary>
        /// Writes the specified <see cref="XdfSection"/> to the buffer
        /// </summary>
        /// <param name="section">The <see cref="XdfSection"/> to write</param>
        public void WriteSection(XdfSection section) {
            WriteBeginSection(section.Name);
            foreach (XdfKey item in section.Keys) {
                WriteKey(item);
            }
            foreach (XdfSection item in section.Sections) {
                WriteSection(item);
            }
            WriteEndSection();
        }
    }
}